﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml.Linq;
using Microsoft.Practices.Unity;
using VisualWip.Core;
using VisualWip.Core.Interfaces;

namespace VisualWip.Util
{
    public class PersistanceProvider
    {
        private readonly IUnityContainer _container;
        private readonly IWorkItemProviderFactory _workItemProviderFactory;

        public PersistanceProvider(IUnityContainer container, IWorkItemProviderFactory workItemProviderFactory)
        {
            _container = container;
            _workItemProviderFactory = workItemProviderFactory;
        }

        public XDocument GetPersistenceDocument(Application application)
        {
            PersistenceTree persistenceTree = application.Persist("Application");
            var root = new XElement("Application");
            AddProperties(persistenceTree, root);
            AddChildren(persistenceTree, root);
            var document = new XDocument();
            document.Add(root);
            return document;
        }

        public void LoadPercistanceDocument(XDocument document, Application application)
        {
            XElement x = document.Root;
            string assemblyQualifiedName = application.GetType().AssemblyQualifiedName;
            var tree = new PersistenceTree("Application", assemblyQualifiedName, () => Create(assemblyQualifiedName));
            ReadProperties(x.Elements("Property"), tree);
            ReadChildren(x.Elements("Child"), tree);

            //TODO:Initiara alla WorkItemProvider
            _workItemProviderFactory.Create("Tfs2010WorkItemWorkProvider");
            
            application.Load(tree);
        }

        private void ReadChildren(IEnumerable<XElement> elements, PersistenceTree tree)
        {
            foreach (XElement element in elements)
            {
                string name = element.Element("Name").Value;
                string typeName = element.Element("Type").Value;
                var settingsDictionary = new PersistenceTree(name, typeName, () => Create(typeName));
                tree.AddChild(settingsDictionary);
                ReadProperties(element.Elements("Property"), settingsDictionary);
                ReadChildren(element.Elements("Child"), settingsDictionary);
            }
        }

        private void ReadProperties(IEnumerable<XElement> elements, PersistenceTree tree)
        {
            foreach (XElement element in elements)
            {
                string typeName = element.Element("Type") != null ? element.Element("Type").Value : string.Empty;
                string propertyName = element.Element("Name") != null ? element.Element("Name").Value : string.Empty;
                string value = element.Element("Value") != null ? element.Element("Value").Value : string.Empty;

                //TODO: Build responibilty chaine here
                if (typeName == "System.String")
                {
                    tree.Add(propertyName, value);
                    continue;
                }
                if (typeName == "System.Boolean")
                {
                    tree.Add(propertyName, bool.Parse(value));
                    continue;
                }
                if (typeName == "System.Guid")
                {
                    tree.Add(propertyName, Guid.Parse(value));
                    continue;
                }
                if (typeName == "System.Int32" || typeName == "System.Int64" || typeName == "System.Int16")
                {
                    tree.Add(propertyName, int.Parse(value));
                    continue;
                }

                if(string.IsNullOrWhiteSpace(typeName))
                    tree.Add(propertyName,string.Empty);
                    continue;

                throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Type: {0} is not handled",
                                                          typeName));
            }
        }

        private void AddChildren(PersistenceTree tree, XElement parent)
        {
            foreach (PersistenceTree child in tree.Children)
            {
                var childElement = new XElement("Child", new XElement("Name", child.Name),
                                                new XElement("Type", child.Type.AssemblyQualifiedName));
                AddProperties(child, childElement);
                AddChildren(child, childElement);
                parent.Add(childElement);
            }
        }

        private void AddProperties(PersistenceTree tree, XElement parent)
        {
            foreach (var property in tree.Properties)
            {
                var propertyElement = new XElement("Property", new XElement("Name", property.Key),
                                                   new XElement("Type",
                                                                property.Value != null
                                                                    ? property.Value.GetType().FullName
                                                                    : string.Empty),
                                                   new XElement("Value",
                                                                property.Value != null
                                                                    ? property.Value.ToString()
                                                                    : string.Empty));
                parent.Add(propertyElement);
            }
        }

        private object Create(string typeName)
        {
            Type type = Type.GetType(typeName);
            return _container.Resolve(type);
        }
    }
}